home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / bbs_util / smsg_102.zip / SENDMSG.C < prev    next >
C/C++ Source or Header  |  1996-04-07  |  22KB  |  825 lines

  1. /*
  2.  * SENDMSG: Utility to send messages for *.MSG or *.SQ? bases
  3.  *
  4.  * Created: 05/Dec/92
  5.  * Updated: 07/Apr/96
  6.  *
  7.  * Written by Pete Kvitek of JV Dialogue 1st BBS (2:5020/6) +7-095-329-2192
  8.  * Copyright (c) 1992-1996 by JV DIALOGUE. All rights reserved.
  9.  *
  10.  * History:
  11.  *
  12.  * 07/Apr/96    -- v1.02
  13.  *        Ported code to MSC 6.0a to compile for DOS and OS/2 1.xx
  14.  *              Updated code to use Scott's SQDEV200 instead of MSGAPI0
  15.  *              Implemented more robuts ^aMSGID time stamp generation
  16.  *        Changed ^aMSGID generation so that now it is not optional
  17.  *        Added ^aPID kludge generation
  18.  *
  19.  * 05/Feb/93    -- v1.01
  20.  *              Corrected 'grunged date' problem
  21.  *              Added optional ^aMSGID generation
  22.  *
  23.  * 05/Dec/92    -- v1.00
  24.  *              Originally written
  25.  *
  26.  */
  27.  
  28.  // COMPILATION NOTES:
  29.  // The SendMsg code was compiled in LARGE memory model using
  30.  // Borland's C++ and Microsoft C v6.00a, however newer
  31.  // versions may serve as well -- at least I hope so...
  32.  
  33. #ifdef __OS2__
  34. #define INCL_NOCOMMON
  35. #define INCL_NOPM
  36. #define INCL_DOSFILEMGR
  37. #define INCL_DOSMEMMGR
  38. #define INCL_DOSINFOSEG
  39. #define INCL_DOSPROCESS
  40. #include <os2.h>
  41. #define OS_2
  42. #endif
  43.  
  44. #include <io.h>
  45. #include <dos.h>
  46. #include <time.h>
  47. #include <ctype.h>
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include <direct.h>
  52.  
  53.  // Remove the EXPENTRY definition since the Scott's API has one too.
  54.  // Both appear to be identical except for the far call specification,
  55.  // but since we're compiling in large model this is not a problem...
  56.  
  57. #undef EXPENTRY
  58. #include "msgapi.h"
  59.  
  60. /////////////////////////////////////////////////////////////////////////////
  61. // M o d u l e   d e c l a r a t i o n s                                   //
  62. /////////////////////////////////////////////////////////////////////////////
  63.  
  64.  // Some useful defines
  65.  
  66.  #define loop while (1)                // Endless loop 'till break
  67.  #define numbof(a)  (sizeof(a)/sizeof(a[0]))    // Number of elements
  68.  #define lengof(s)  (sizeof(s) - 1)        // Length of the string
  69.  
  70.  #define TRUE         1
  71.  #define FALSE         0
  72.  
  73. #ifndef __OS2__
  74.  #define BOOL         int
  75.  #define ULONG         unsigned long
  76. #endif
  77.  
  78.  // Some compiler dependant stuff
  79.  
  80. #if defined(__TURBOC__) || defined(__BORLANDC__)
  81. #define FN_MAXPATH      MAXPATH
  82. #define FN_MAXDRIVE     MAXDRIVE
  83. #define FN_MAXDIR       MAXDIR
  84. #define FN_MAXFILE      MAXFILE
  85. #define FN_MAXEXT       MAXEXT
  86. #elif defined(__MSC__)
  87. #define FN_MAXPATH     _MAX_PATH
  88. #define FN_MAXDRIVE    _MAX_DRIVE
  89. #define FN_MAXDIR      _MAX_DIR
  90. #define FN_MAXFILE     _MAX_FNAME
  91. #define FN_MAXEXT      _MAX_EXT
  92. #else
  93. #error Unknown complier
  94. #endif
  95.  
  96.  // Miscellaneous defines
  97.  
  98.  #define VERSION      "1.02"            // Revision level
  99.  
  100. #ifndef __OS2__
  101.  #define SMSG_NAME    "SendMsg"        // Utility name (dos)
  102. #else
  103.  #define SMSG_NAME    "SendMsg/2"        // Utility name (os/2)
  104. #endif
  105.  
  106.  #define SMSG_PID       "\x01""PID: "SMSG_NAME" v"VERSION // PID kludge
  107.  #define SMSG_TEARLINE  "\r--- "SMSG_NAME"\r"         // Tear line
  108.  
  109.  #define MSGTEXT    stdin            // Message text input handle
  110.  #define AUXTEXT    stdout            // Auxiliary text output handle
  111.  
  112.  #define ISOPTION(ch)  ((ch)=='-'||(ch)=='/')    // Command line option prefix
  113.  
  114.  // Program control flags in 'fsFlags'
  115.  
  116.  #define FL_SQUISHMAIL    0x0001            // Squish mail folder
  117.  
  118.  // Module variables
  119.  
  120.  static char achPath[FN_MAXPATH];        // Path to mail folder
  121.  static unsigned fsFlags;            // Control flags FL_
  122.  static HAREA harea;                // Mail folder handle
  123.  static XMSG msg;                // Message header info
  124.  
  125.  // Message attribute table
  126.  
  127.  static struct {
  128.    dword attr;        // Attribute bit
  129.    char * psz;        // Attribute name string
  130.    char ch;        // Command line option char or null if none
  131.  } aMsgAttr[] = {
  132.    MSGPRIVATE,        "Pvt",        'P',
  133.    MSGCRASH,        "Crash",    'C',
  134.    MSGREAD,        "Recv",         0,
  135.    MSGSENT,        "Sent",         0,
  136.    MSGFILE,        "File",        'F',
  137.    MSGFWD,        "Transit",     0,
  138.    MSGORPHAN,        "Orphan",     0,
  139.    MSGKILL,        "Kill",        'K',
  140.    MSGLOCAL,        "Local",     0,
  141.    MSGHOLD,        "Hold",        'H',
  142.    MSGXX2,        "Rsvd2",     0,
  143.    MSGFRQ,        "Frq",        'Q',
  144.    MSGRRQ,        "Rrq",        'R',
  145.    MSGCPT,        "Cpt",         0,
  146.    MSGARQ,        "Arq",        'A',
  147.    MSGURQ,        "Urq",        'U',
  148.    MSGSCANNED,        "Scn",        'S',
  149.  };
  150.  
  151. /////////////////////////////////////////////////////////////////////////////
  152. // M i s c e l l a n e o u s   s u b r o u t i n e s                       //
  153. /////////////////////////////////////////////////////////////////////////////
  154.  
  155. /*
  156.  * This subroutine displays logo
  157.  */
  158.  
  159.  static void DoShowLogo(void)
  160.  {
  161.    fprintf(AUXTEXT,
  162.      "\n"
  163.      "Send Message Utility v"VERSION", "__DATE__", "__TIME__"\n"
  164.      "Written by Pete Kvitek of JV Dialogue 1st BBS, 2:5020/6\n"
  165.      "Copyright (C) 1991-1996 by JV Dialogue. All rights reserved.\n"
  166.      "\n"
  167.    );
  168.  }
  169.  
  170. /*
  171.  * This subroutine displays help
  172.  */
  173.  
  174.  static void DoShowHelp(void)
  175.  {
  176.    int iMsgAttr;
  177.  
  178.    fprintf(AUXTEXT,
  179.      "Usage: SENDMSG <folder> <fromname,addr> [toname,addr] [-a<attr>] [-s<subj>]\n"
  180.      "\n"
  181.      "       folder    - specifies the mail folder path. If preceded\n"
  182.      "                   with '$' then the folder is *.SQ? type base,\n"
  183.      "                   otherwise it's assumed to be *.MSG style base\n"
  184.      "       name,addr - specifies message from/to name and address\n"
  185.      "       attr      - specifies message attributes (P,H,F, etc..)\n"
  186.      "       subj      - specifies message subject\n"
  187.      "\n"
  188.      "If any parameter includes spaces it should be enclosed in double quotas.\n"
  189.      "The message body text file is supposed to be available through the standard\n"
  190.      "input device.  Use redirection or piping characters to set it in.\n"
  191.      "\n"
  192.      "Examples: SENDMSG c:\\mail Pete,2:5020/6 220/501 -aP \"-sNew Files\" < NEWFILE.LST\n"
  193.      "          echo Hi! | SENDMSG c:\\mail 2:5020/6 1.1 -aPF \"-sc:\\autoexec.bat\"\n"
  194.    );
  195.  
  196.    // Show supported attributes
  197.  
  198.    fprintf(AUXTEXT, "\nAttibutes: ");
  199.    for (iMsgAttr = 0; iMsgAttr < numbof(aMsgAttr); iMsgAttr++)
  200.      if (aMsgAttr[iMsgAttr].ch)
  201.        fprintf(AUXTEXT, "%c:%s ", aMsgAttr[iMsgAttr].ch,
  202.                   aMsgAttr[iMsgAttr].psz);
  203.    fprintf(AUXTEXT, "\n");
  204.  }
  205.  
  206. /*
  207.  * This sub routine builds a fully qualified path
  208.  */
  209.  
  210.  BOOL BuildFullPath(char * pszDest, char * pszSrc)
  211.  {
  212. #ifdef __MSC__
  213.    // Build fill path using comiler service and check if ok
  214.  
  215.    if (_fullpath(pszDest, pszSrc, FN_MAXPATH) == NULL)
  216.      return FALSE;
  217. #else
  218.    char achDrive[FN_MAXDRIVE];
  219.    char achDir[FN_MAXDIR];
  220.    char achFile[FN_MAXFILE];
  221.    char achExt[FN_MAXEXT];
  222.    char achCurDir[FN_MAXDIR];
  223.    int iCurDrive;
  224.  
  225.    // Decompose supplied path
  226.  
  227.    fnsplit(pszSrc, achDrive, achDir, achFile, achExt);
  228.  
  229.    // Preserve current drive
  230.  
  231.    iCurDrive = getdisk();
  232.  
  233.    // Check if drive specified in the supplied path and if not,
  234.    // assume the current one
  235.  
  236.    if (!achDrive[0]) {
  237.      strcpy(achDrive, "A:");
  238.      achDrive[0]+= (char) iCurDrive;
  239.    }
  240.  
  241.    // Set the current drive to the requested one and check if ok.
  242.    // If failed, restore orginal drive and return error
  243.  
  244.    setdisk(toupper(achDrive[0]) - 'A');
  245.    if (getdisk() != toupper(achDrive[0]) - 'A') {
  246.      setdisk(iCurDrive);
  247.      return FALSE;
  248.    }
  249.  
  250.    // Preserve the current directory on the reqested drive and
  251.    // check if ok, otherwise restore initial current drive and return
  252.  
  253.    strcpy (achCurDir, "\\");
  254.    if (getcurdir(0, &achCurDir[1])) {
  255.      setdisk(iCurDrive);
  256.      return FALSE;
  257.    }
  258.  
  259.    // Check if directory specified and make it current
  260.  
  261.    if (achDir[0]) {
  262.  
  263.      // Kill trailing back slash if it's not the only character
  264.      // of the directory specification
  265.  
  266.      if ((achDir[strlen(achDir) - 1] == '\\') && (strlen(achDir) > 1 ))
  267.        achDir[strlen(achDir) - 1] = '\0';
  268.  
  269.      // Change to the specified directory and check if ok. If failed,
  270.      // restore the inital directory on the requested drive and change
  271.      // to the initial drive
  272.  
  273.      if (chdir(achDir)) {
  274.        chdir(achCurDir);
  275.        setdisk(iCurDrive);
  276.        return FALSE;
  277.      }
  278.    }
  279.  
  280.    // So we managed to make a requested directory current on the
  281.    // requested drive. Now get its full specification and this
  282.    // will be what we're after. If failed, just restore things back
  283.  
  284.    strcpy(achDir, "\\");
  285.    if (getcurdir(0, &achDir[1])) {
  286.      chdir(achCurDir);
  287.      setdisk(iCurDrive);
  288.      return FALSE;
  289.    }
  290.  
  291.    // Compose the fully qualified file name and restore
  292.    // the inital directory on the requested drive and change
  293.    // to the initial drive
  294.  
  295.    fnmerge(pszDest, achDrive, achDir, achFile, achExt);
  296.    chdir(achCurDir);
  297.    setdisk(iCurDrive);
  298. #endif
  299.  
  300.    return TRUE;
  301.  }
  302.  
  303. /*
  304.  * This routine scans in z:n/n.p address specification
  305.  */
  306.  
  307.  static char * DoScanNetAddr(NETADDR * pnetAddr, char * psz)
  308.  {
  309.    char * pch, * pchEnd, * pchNext, ch;
  310.  
  311.    // Skip through the leading spaces and fix up the end
  312.  
  313.    for (pch = psz; isspace(*pch); pch++);
  314.    for (pchNext = pch; *pchNext && !isspace(*pchNext); pchNext++);
  315.    ch = *pchNext; *pchNext = '\0'; pchEnd = pch;
  316.  
  317.    // Scan in the zone if any
  318.  
  319.    if (*pch && !isspace(*pch) && strchr(pch, ':')) {
  320.      while (isdigit(*pchEnd)) pchEnd++;
  321.      if (*pchEnd != ':' || pch == pchEnd) return NULL;
  322.      pnetAddr->zone = atoi(pch);
  323.      pch = ++pchEnd;
  324.      if (!isdigit(*pch) || !strchr(pch, '/')) return NULL;
  325.    }
  326.  
  327.    // Scan in the net if any
  328.  
  329.    if (*pch && !isspace(*pch) && strchr(pch, '/')) {
  330.      while (isdigit(*pchEnd)) pchEnd++;
  331.      if (*pchEnd != '/' || pch == pchEnd) return NULL;
  332.      pnetAddr->net = atoi(pch);
  333.      pch = ++pchEnd;
  334.      if (!isdigit(*pch)) return NULL;
  335.    }
  336.  
  337.    // Scan in the node if any
  338.  
  339.    if (*pch && !isspace(*pch) && *pch != '.') {
  340.      while (isdigit(*pchEnd)) pchEnd++;
  341.      if (*pchEnd != '.' && !isspace(*pchEnd) && *pchEnd) return NULL;
  342.      pnetAddr->node = atoi(pch);
  343.      pch = pchEnd;
  344.    }
  345.  
  346.    // Scan in the point if any
  347.  
  348.    if (*pch != '.') {
  349.      if (!isspace(*pch) && *pch) return NULL;
  350.      pnetAddr->point = 0;
  351.    } else {
  352.      for (pchEnd = ++pch; isdigit(*pchEnd); pchEnd++);
  353.      if (!isspace(*pchEnd) && *pchEnd) return NULL;
  354.      pnetAddr->point = atoi(pch);
  355.      pch = pchEnd;
  356.    }
  357.  
  358.    // Restore the zeroed trailing characters
  359.  
  360.    *pchNext = ch;
  361.  
  362.    // Check if zone or net is zero and return
  363.  
  364.    return (pnetAddr->zone && pnetAddr->net) ? pchNext : NULL;
  365.  }
  366.  
  367. /*
  368.  * This subroutine scans name and network address
  369.  */
  370.  
  371.  static BOOL DoScanNameAddr(char * psz, NETADDR * pnetAddr,
  372.                             char * pszName, short cchName) 
  373.  {
  374.    char * pch;
  375.    short cch;
  376.  
  377.    // Check if there is a name and scan it in
  378.  
  379.    if (isdigit(*psz))
  380.      pch = psz;
  381.    else
  382.      if ((pch = strchr(psz, ',')) == NULL) {
  383.        return FALSE;
  384.      } else {
  385.        cch = min((short)(pch - psz), cchName);
  386.        memcpy(pszName, psz, cch);
  387.        pszName[cch] = '\0';
  388.        pch++;
  389.      }
  390.  
  391.    // Scan in the network address if any
  392.  
  393.    DoScanNetAddr(pnetAddr, pch);
  394.  
  395.    return TRUE;
  396.  }
  397.  
  398. /*
  399.  * This subroutine scans message attribute specification
  400.  */
  401.  
  402.  static void DoScanMsgAttr(char * psz, dword * pattr)
  403.  {
  404.    short iAttr;
  405.  
  406.    // Scan through all the specified message attributes
  407.  
  408.    for (; *psz; psz++) {
  409.      for (iAttr = 0; toupper(*psz) != aMsgAttr[iAttr].ch; iAttr++)
  410.        if (iAttr >= numbof(aMsgAttr)) {
  411.      fprintf(AUXTEXT, "Unknown message attribute: '%s'\n", psz);
  412.      exit(EXIT_FAILURE);
  413.        }
  414.      *pattr|= aMsgAttr[iAttr].attr;
  415.    }
  416.  }
  417.  
  418. /*
  419.  * This subroutine prints out a message header
  420.  */
  421.  
  422.  static void DoShowMsgHeader(XMSG * pmsg)
  423.  {
  424.    int iAttr;
  425.    // Print out the message header
  426.  
  427.    fprintf(AUXTEXT,
  428.        "  To: %s, %u:%u/%u.%u\n"
  429.        "From: %s, %u:%u/%u.%u\n"
  430.        "Subj: %s\n",
  431.        pmsg->to,   pmsg->dest.zone, pmsg->dest.net, pmsg->dest.node, pmsg->dest.point,
  432.        pmsg->from, pmsg->orig.zone, pmsg->orig.net, pmsg->orig.node, pmsg->orig.point,
  433.        pmsg->subj
  434.       );
  435.  
  436.    // Print out the message attribute if any
  437.  
  438.    if (pmsg->attr) {
  439.      fprintf(AUXTEXT, "Attr:");
  440.      for (iAttr = 0; iAttr < numbof(aMsgAttr); iAttr++)
  441.        if (pmsg->attr & aMsgAttr[iAttr].attr)
  442.      fprintf(AUXTEXT, " %s", aMsgAttr[iAttr].psz);
  443.      putc('\n', AUXTEXT);
  444.    }
  445.  }
  446.  
  447. /*
  448.  * This subroutine to process the command line parameters
  449.  */
  450.  
  451.  static void DoProcCmdLine(int cArg, char * apszArg[])
  452.  {
  453.    char * psz;
  454.    short iArg = 1;
  455.  
  456.    // Display the logo screen and check if there are any
  457.    // command line parameters specified and if not, display
  458.    // help screen and quit now...
  459.  
  460.    DoShowLogo();
  461.    if (cArg == 1) {
  462.      DoShowHelp();
  463.      exit(EXIT_FAILURE);
  464.    }
  465.  
  466.    // Scan in the mail folder path specification and check if it's
  467.    // a *.SQ? type database
  468.  
  469.    if (apszArg[iArg][0] == '$') {
  470.      psz = &apszArg[iArg][1]; fsFlags|= FL_SQUISHMAIL;
  471.    } else {
  472.      psz = &apszArg[iArg][0];
  473.    }
  474.  
  475.    // Build the fully qualified path and make it upper case
  476.  
  477.    if (!BuildFullPath(achPath, psz)) {
  478.      fprintf(AUXTEXT, "Invalid mail folder path: '%s'\n", psz);
  479.      exit(EXIT_FAILURE);
  480.    } else {
  481. #ifndef __OS2__
  482.      strupr(achPath);
  483. #endif
  484.      iArg++;
  485.    }
  486.  
  487.    // Scan in the 'From' name/address
  488.  
  489.    if (iArg >= cArg) {
  490.      fprintf(AUXTEXT, "Missing 'From' address/name\n");
  491.      exit(EXIT_FAILURE);
  492.    } else
  493.      if (!DoScanNameAddr(apszArg[iArg], &msg.orig, msg.from, lengof(msg.from)) ||
  494.       msg.orig.zone == 0 || msg.orig.net == 0) {
  495.        fprintf(AUXTEXT, "Invalid 'From' address: '%s'\n", apszArg[iArg]);
  496.        exit(EXIT_FAILURE);
  497.      } else {
  498.        memcpy(&msg.dest, &msg.orig, sizeof(msg.dest));
  499.        iArg++;
  500.      }
  501.  
  502.    // Scan in the 'To' name/address if any
  503.  
  504.    if (iArg >= cArg || ISOPTION(apszArg[iArg][0])) {
  505.      memcpy(&msg.dest, &msg.orig, sizeof(msg.dest));
  506.    } else
  507.      if (!DoScanNameAddr(apszArg[iArg], &msg.dest, msg.to, lengof(msg.to)) ||
  508.       msg.dest.zone == 0 || msg.dest.net == 0) {
  509.        fprintf(AUXTEXT, "Invalid 'To' address: '%s'\n", apszArg[iArg]);
  510.        exit(EXIT_FAILURE);
  511.      } else
  512.        iArg++;
  513.  
  514.    // Process the command line options if any
  515.  
  516.    for (; iArg < cArg; iArg++)
  517.      if (!ISOPTION(apszArg[iArg][0])) {
  518.        fprintf(AUXTEXT, "Invalid option: '%s'\n", apszArg[iArg]);
  519.        exit(EXIT_FAILURE);
  520.      } else
  521.        switch (tolower(apszArg[iArg][1])) {
  522.      case 's': // Message subject
  523.            strncpy(msg.subj, &apszArg[iArg][2], lengof(msg.subj));
  524.            break;
  525.      case 'a': // Message attributes
  526.            DoScanMsgAttr(&apszArg[iArg][2], &msg.attr);
  527.            break;
  528.      default:  fprintf(AUXTEXT, "Unknown option: '%s'\n", apszArg[iArg]);
  529.            exit(EXIT_FAILURE);
  530.        }
  531.  
  532.    // Set up default message header strings if not defined yet
  533.  
  534.    if (!msg.to[0]) strcpy(msg.to, msg.attr & MSGPRIVATE ? "SysOp" : "All");
  535.    if (!msg.from[0]) strcpy(msg.from, "SysOp");
  536.    if (!msg.subj[0]) strcpy(msg.subj, "<none>");
  537.  
  538.    // Explicitely set the 'Local' message attribute
  539.  
  540.    msg.attr|= MSGLOCAL;
  541.  }
  542.  
  543. /*
  544.  * This subroutine returns an API error string
  545.  */
  546.  
  547.  static char * DoGetApiError(void)
  548.  {
  549.    switch (msgapierr) {
  550.      case MERR_NOMEM: return "not enough memory";
  551.      case MERR_NOENT: return "area does not exist";
  552.      case MERR_BADF:  return "area is damaged";
  553.      default:         return "error unknown";
  554.    }
  555.  }
  556.  
  557. /*
  558.  * This subroutine opens a mail folder
  559.  */
  560.  
  561.  static BOOL DoOpenMailFolder(void)
  562.  {
  563.    struct _minf minf;
  564.  
  565.    // Initialize the Scott's API and check if ok
  566.  
  567.    minf.req_version = 0;
  568.    minf.def_zone = msg.orig.zone;
  569.  
  570.    if (MsgOpenApi(&minf) == -1) {
  571.      fprintf(AUXTEXT, "MsgAPI initialization failed\n");
  572.      exit(EXIT_FAILURE);
  573.    }
  574.  
  575.    // Open the mail folder, lock it and check if ok
  576.  
  577.    if ((harea = MsgOpenArea( achPath
  578.                , MSGAREA_NORMAL
  579.                , fsFlags & FL_SQUISHMAIL ? MSGTYPE_SQUISH : MSGTYPE_SDM
  580.                )) == NULL) {
  581.      fprintf(AUXTEXT, "Can't open folder %s%s -- %s\n",
  582.          achPath, fsFlags & FL_SQUISHMAIL ? ".SQ?" : "\\*.MSG",
  583.          DoGetApiError());
  584.      exit(EXIT_FAILURE);
  585.    }
  586.  
  587.    return TRUE;
  588.  }
  589.  
  590. /*
  591.  * This subroutine closes a mail folder
  592.  */
  593.  
  594.  static BOOL DoCloseMailFolder(void)
  595.  {
  596.    // Check if opened
  597.  
  598.    if (harea == NULL)
  599.      return FALSE;
  600.  
  601.    // Unlock and close the netmail folder and check if ok
  602.  
  603.    if (MsgCloseArea(harea) == -1) {
  604.      fprintf(AUXTEXT, "Can't close mail folder: %s\n", DoGetApiError());
  605.      exit(EXIT_FAILURE);
  606.    }
  607.  
  608.    // Shut down the Scott's API
  609.  
  610.    if (MsgCloseApi() == -1) {
  611.      fprintf(AUXTEXT, "MsgAPI shutdown failed\n");
  612.      exit(EXIT_FAILURE);
  613.    }
  614.  
  615.    return TRUE;
  616.  }
  617.  
  618. /*
  619.  * This subroutine sets message creation date/stamp
  620.  */
  621.  
  622.  static void DoSetMsgDateTime(XMSG * pmsg)
  623.  {
  624.    time_t sec;
  625.    struct tm * ptime;
  626.  
  627.    // Get the current time
  628.  
  629.    sec = time(NULL); ptime = localtime(&sec);
  630.  
  631.    // Set message date time stamp
  632.  
  633.    pmsg->date_written.date.yr = ptime->tm_year - 80;
  634.    pmsg->date_written.date.mo = ptime->tm_mon + 1;
  635.    pmsg->date_written.date.da = ptime->tm_mday;
  636.  
  637.    pmsg->date_written.time.hh = ptime->tm_hour;
  638.    pmsg->date_written.time.mm = ptime->tm_min;
  639.    pmsg->date_written.time.ss = ptime->tm_sec / 2;
  640.  }
  641.  
  642. /*
  643.  * This subroutine makes up a unique ^aMSGID stamp
  644.  */
  645.  
  646.   static ULONG DoMakeMSGIDStamp(void)
  647.   {
  648.     static ULONG lStampPrev;
  649.     ULONG lStamp, lSecs, lHund;
  650.     unsigned iLoop = 0;
  651. #ifdef __OS2__
  652.     static BOOL fInfoSeg = FALSE;
  653.     static PGINFOSEG pgis;
  654.     static PLINFOSEG plis;
  655.     SEL selgis, sellis;
  656. #else
  657.     union REGS regs;
  658. #endif
  659.  
  660.     // Under OS2 get pointers to the global and local info segments once
  661.  
  662. #ifdef __OS2__
  663.     if (!fInfoSeg) {
  664.       DosGetInfoSeg(&selgis, &sellis);
  665.       pgis = MAKEPGINFOSEG(selgis);
  666.       plis = MAKEPLINFOSEG(sellis);
  667.       fInfoSeg = TRUE;
  668.     }
  669. #endif
  670.  
  671.     // Make up time stamp out of number of seconds since Jan 1, 1970
  672.     // shifted 7 bits to the left OR'ed with current system clock and
  673.     // loop untill we get a new stamp
  674.  
  675.     do {
  676. #ifdef __OS2__
  677.       lSecs = (ULONG) pgis->time;
  678.       lHund = (ULONG) pgis->hundredths;
  679.       DosSleep(0);
  680. #else
  681.       lSecs = (ULONG) time(NULL);
  682.       regs.h.ah = 0x2c; intdos(®s, ®s);
  683.       lHund = (ULONG) regs.h.dl;
  684. #endif
  685.       lStamp = (lSecs << 7) | (lHund & 0x07f);
  686.     } while ((lStampPrev >= lStamp) && (++iLoop < 0x7fff));
  687.  
  688.     // Check if we finally have unique ascending ^aMSGID kludge stamp
  689.     // ATTN: What may be reasonable action here?
  690.  
  691.     if (lStampPrev >= lStamp) {
  692.       fprintf(AUXTEXT, "Can't create unique ^aMSGID lStamp=%08lx, lStampPrev=%08lx\n\n\a",
  693.             lStamp, lStampPrev);
  694.     }
  695.  
  696.     return lStampPrev = lStamp;
  697.   }
  698.  
  699. /*
  700.  * This subroutine creates a new message
  701.  */
  702.  
  703.  static void DoCreateMessage(XMSG * pmsg)
  704.  {
  705.    long cchCtrl, cchBody;
  706.    short cch, ich;
  707.    char ach[512];
  708.    char * pch;
  709.    HMSG hmsg;
  710.  
  711.    // Get length of the message body. Note that the
  712.    // tear line will not be written for the empty messages
  713.  
  714.    if ((cchBody = filelength(fileno(MSGTEXT))) > 0)
  715.      cchBody+= sizeof(SMSG_TEARLINE);
  716.    else
  717.      cchBody = 0l;
  718.  
  719.    // Open the new message and check if ok
  720.  
  721.    if ((hmsg = MsgOpenMsg(harea, MOPEN_CREATE, 0)) == NULL) {
  722.      fprintf(AUXTEXT, "Can't create message\n");
  723.      exit(EXIT_FAILURE);
  724.    }
  725.  
  726.    // Set the message creation date/time stamp
  727.  
  728.    DoSetMsgDateTime(pmsg);
  729.  
  730.    // Initizlize MSGID stamp generation logic so that we'll not get dupes
  731.    // if running successively on fast machines
  732.  
  733.    DoMakeMSGIDStamp();
  734.  
  735.    // Create ^aMSGID kludge string
  736.  
  737.    sprintf(ach, "\x01""MSGID: %u:%u/%u.%u %08lx",
  738.         pmsg->orig.zone, pmsg->orig.net,
  739.         pmsg->orig.node, pmsg->orig.point,
  740.         DoMakeMSGIDStamp());
  741.  
  742.    // Append PID kludge
  743.  
  744.    strcat(ach, SMSG_PID);
  745.  
  746.    // Calculate control info length including trailing zero
  747.    // required by MsgWriteMsg api
  748.  
  749.    cchCtrl = strlen(ach) + 1;
  750.  
  751.    // Write out the new message header and check if ok
  752.  
  753.    if (MsgWriteMsg(hmsg, FALSE, pmsg, NULL, 0L, cchCtrl + cchBody,
  754.            cchCtrl, ach) == -1) {
  755.      fprintf(AUXTEXT, "Can't write message header or kludges\n");
  756.      MsgCloseMsg(hmsg);
  757.      exit(EXIT_FAILURE);
  758.    }
  759.  
  760.    // Check if we have nothing to write into message body
  761.  
  762.    if (!cchBody) {
  763.  
  764.      // Write out the empty message body
  765.  
  766.      if (MsgWriteMsg(hmsg, TRUE, NULL, "", 1, 0L, 0L, NULL) == -1) {
  767.        fprintf(AUXTEXT, "Can't write empty message body\n");
  768.        MsgCloseMsg(hmsg);
  769.        exit(EXIT_FAILURE);
  770.      }
  771.    } else {
  772.  
  773.      // Write out new message text if any. Replace all \n with \r
  774.      // in order to conform FidoNet(tm) message body standards
  775.  
  776.      while ((cch = fread(ach, 1, sizeof(ach), MSGTEXT)) > 0) {
  777.        for (pch = ach, ich = 0; ich < cch; pch++, ich++)
  778.      if (*pch == '\n') *pch = '\r';
  779.        if (MsgWriteMsg(hmsg, TRUE, NULL, ach, cch, 0L, 0L, NULL) == -1) {
  780.      fprintf(AUXTEXT, "Can't write message body\n");
  781.      MsgCloseMsg(hmsg);
  782.      exit(EXIT_FAILURE);
  783.        }
  784.      }
  785.  
  786.      // Write out the tear line. Note that the trailing null is also
  787.      // written since we're using 'sizeof()'
  788.  
  789.      if (MsgWriteMsg(hmsg, TRUE, NULL, SMSG_TEARLINE, sizeof(SMSG_TEARLINE),
  790.              0L, 0L, NULL) == -1) {
  791.        fprintf(AUXTEXT, "Can't write tear line\n");
  792.        MsgCloseMsg(hmsg);
  793.        exit(EXIT_FAILURE);
  794.      }
  795.    }
  796.  
  797.    // Close the message just created and display the results
  798.  
  799.    MsgCloseMsg(hmsg);
  800.  
  801.    DoShowMsgHeader(&msg);
  802.  
  803.    fprintf(AUXTEXT, "Body: %lu byte(s)\n"
  804.             "Mail: %s%s\n",
  805.              cchBody, achPath,
  806.              fsFlags & FL_SQUISHMAIL ? ".SQ?" : "\\*.MSG");
  807.  }
  808.  
  809. /////////////////////////////////////////////////////////////////////////////
  810. // M a i n . . .                                                           //
  811. /////////////////////////////////////////////////////////////////////////////
  812.  
  813.  void main(int argc, char * argv[])
  814.  {
  815.    DoProcCmdLine(argc, argv);
  816.    DoOpenMailFolder();
  817.    DoCreateMessage(&msg);
  818.    DoCloseMailFolder();
  819.    exit(EXIT_SUCCESS);
  820.  }
  821.  
  822. /*
  823.  * End of SENDMSG.C
  824.  */
  825.